﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Krilib.Data;
using Krilib.Control.Base;
using System.Windows.Forms;

namespace Krile.Forms.MainForm.Controls.Timeline
{
    public class TimelineListRenderer
    {
        private void Redraw(bool remeasure)
        {
            Core.FormManager.MainForm.RefreshTL(remeasure);
        }

        public TimelineMouseControllableItem[] Items
        {
            get
            {
                return MCItemFactory().ToArray<TimelineMouseControllableItem>();
            }
        }

        readonly TimelineList parent;
        public TimelineListRenderer(TimelineList par)
        {
            parent = par;
        }

        public int NormalItemHeight
        {
            get
            {
                if (Core.Config != null)
                    return Core.Config.Appearance.Renderer.Detail.NormalListHeight;
                else
                    return 0;
            }
        }

        int _selItemHeight = 0;
        public int SelectedItemHeight
        {
            get { return _selItemHeight; }
            private set { _selItemHeight = value; }
        }

        public int ListHeight
        {
            get
            {
                if (parent == null || parent.Statuses == null) return 0;
                if (parent.SelectedIndex == -1)
                    return NormalItemHeight * parent.Statuses.Length;
                else
                    return NormalItemHeight * (parent.Statuses.Length - 1) + SelectedItemHeight;
            }
        }

        public void DrawNormalItem(Graphics g, int index, int vpos, int width)
        {
            var status = parent.Statuses[index].BaseStatus;
            Rectangle rect = new Rectangle(0, vpos, width, NormalItemHeight);
            using (var b = new SolidBrush(
                index % 2 == 0 ?
                Color.White :
                Core.Config.Appearance.Renderer.Colors.ListAlternateBackgroundColor.Value
                ))
                g.FillRectangle(b, rect);

            Rectangle nameRect = new Rectangle(rect.Location, new Size(Core.Config.Appearance.Renderer.UserNameArea, rect.Height));
            using (var b = new SolidBrush(Color.FromArgb(150, status.ParentService.ServiceColor)))
                g.FillRectangle(b, nameRect);

            Rectangle textRect = new Rectangle(rect.Location, new Size(rect.Width - nameRect.Width, rect.Height));
            textRect.X += nameRect.Width;

            if (Core.Config.Appearance.Renderer.UseListMiniIcon)
            {
                Image i = GetImageWithTLCtlAutomation(status.user.iconUri, false);
                int rectSize = nameRect.Width <= nameRect.Height ? nameRect.Width : nameRect.Height;
                int vmargin = nameRect.Width <= nameRect.Height ? (nameRect.Height - nameRect.Width) / 2 : 1;
                rectSize -= vmargin * 2;
                Rectangle imgDest = new Rectangle(rect.Location, new Size(rectSize, rectSize));
                imgDest.X += 1;
                imgDest.Y += vmargin;
                g.DrawImage(i, imgDest);
                nameRect.X += imgDest.Width + 2;
                nameRect.Width -= imgDest.Width + 2;
            }
            TextRenderer.DrawText(
                g,
                status.user.name,
                SystemFonts.DefaultFont,
                nameRect,
                status.ParentAgent.HostingService.ForeColor,
                TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);

            if (status.text.HeaderText != null && status.text.HeaderText != String.Empty)
            {
                int strWidth = TextRenderer.MeasureText(
                    status.text.HeaderText,
                    SystemFonts.DefaultFont,
                    new Size(),
                    TextFormatFlags.HorizontalCenter | TextFormatFlags.Left | TextFormatFlags.NoPrefix
                    ).Width;
                Rectangle htRect = new Rectangle(
                    textRect.X,
                    textRect.Y + 1,
                    strWidth < textRect.Width ? strWidth : textRect.Width,
                    textRect.Height - 2);
                g.FillRectangle(Brushes.Gray, htRect);
                TextRenderer.DrawText(
                    g, status.text.HeaderText,
                    SystemFonts.DefaultFont,
                    htRect,
                    Color.White,
                    TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);
                textRect.X += htRect.Width;
                textRect.Width -= htRect.Width;
            }

            if (status.text.FooterText != null && status.text.FooterText != String.Empty)
            {
                int strWidth = TextRenderer.MeasureText(
                    g, status.text.FooterText,
                    SystemFonts.DefaultFont,
                    new Size(),
                    TextFormatFlags.HorizontalCenter | TextFormatFlags.Left | TextFormatFlags.NoPrefix
                    ).Width;
                Rectangle ftRect = new Rectangle(
                    0,
                    textRect.Y + 1,
                    strWidth < textRect.Width ? strWidth : textRect.Width,
                    textRect.Height - 2);
                ftRect.X = textRect.Right - ftRect.Width;
                g.FillRectangle(Brushes.Gray, ftRect);
                TextRenderer.DrawText(
                    g, status.text.FooterText,
                    SystemFonts.DefaultFont,
                    ftRect,
                    Color.White,
                    TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);
                textRect.Width -= ftRect.Width;
            }
            
            TextRenderer.DrawText(
                g,
                status.text.Original,
                SystemFonts.DefaultFont,
                textRect,
                Color.Black,
                TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);
            return;
        }

        private List<TimelineMouseControllableItem> selectedItemMCItemList = new List<TimelineMouseControllableItem>();
        public void DrawSelectedItem(Graphics g, int index, int vpos, int width)
        {
            lock (selectedItemMCItemList)
            {
                selectedItemMCItemList.Clear();
                var status = parent.Statuses[index].BaseStatus;
                Rectangle rect = new Rectangle(0, vpos, width, SelectedItemHeight);
                using (var b = new SolidBrush(
                    status.ParentService.ServiceColor
                    ))
                    g.FillRectangle(b, rect);

                //-------------------------------------------------------------
                //Icon draw
                DrawSelectedStatusIconArea(g, status, ref rect);

                if (rect.Width < 0)
                    return;

                //-------------------------------------------------------------
                //Name draw
                DrawSelectedStatusNameArea(g, status, ref rect);

                //-------------------------------------------------------------
                //Reply draw
                DrawSelectedStatusReplyArea(g, status, ref rect);

                using (var b = new SolidBrush(status.ParentService.SecondaryColor))
                    g.FillRectangle(b, rect);
                rect.X++;
                rect.Y++;
                status.text.Draw(g, rect.Location, Krilib.Control.XString.DrawMode.None);
            }
            return;
        }

        private void DrawSelectedStatusIconArea(Graphics g, Status status, ref Rectangle rect)
        {
            Rectangle pictRect = new Rectangle(
                0,
                rect.Y,
                Core.Config.Appearance.Renderer.UserNameArea < rect.Width ?
                    Core.Config.Appearance.Renderer.UserNameArea : rect.Width,
                rect.Height);
            pictRect.X++;
            pictRect.Y++;
            pictRect.Width -= 2;
            pictRect.Height -= 2;
            using (var b = new SolidBrush(Color.FromArgb(50, Color.White)))
                g.FillRectangle(b, pictRect);

            if (pictRect.Height < pictRect.Width)
            {
                pictRect.X += (pictRect.Width - pictRect.Height) / 2;
                pictRect.Width = pictRect.Height;
            }
            else if (pictRect.Width < pictRect.Height)
            {
                pictRect.Y += (pictRect.Height - pictRect.Width) / 2;
                pictRect.Height = pictRect.Width;
            }
            Image i = GetImageWithTLCtlAutomation(status.user.iconUri, false);
            g.DrawImage(i, pictRect);

            rect.Width -= Core.Config.Appearance.Renderer.UserNameArea;
            rect.X += Core.Config.Appearance.Renderer.UserNameArea;
        }

        private void DrawSelectedStatusNameArea(Graphics g, Status status, ref Rectangle rect)
        {
            //Init
            Rectangle unRect = rect;
            unRect.Height = NormalItemHeight;
            rect.Y += unRect.Height;

            //*******************************************
            //Command button rendering
            //*******************************************
            //Create destination rectangle
            //Command button is 18x18
            if (status.commands != null && status.commands.Length > 0)
            {
                Rectangle commandButtonArea = new Rectangle(
                    0,
                    rect.Y,
                    status.commands.Length * Krilib.Control.CommandButtonRenderer.ButtonWHSize,
                    Krilib.Control.CommandButtonRenderer.ButtonWHSize);
                if (rect.Width < commandButtonArea.Width)
                    commandButtonArea.Width = rect.Width;
                commandButtonArea.X = rect.Right - commandButtonArea.Width;
                unRect.Width -= commandButtonArea.Width;
                int hDist = NormalItemHeight - Krilib.Control.CommandButtonRenderer.ButtonWHSize;
                commandButtonArea.Y += hDist / 2;
                for (int i = 0;
                    i < status.commands.Length && commandButtonArea.Width > 0;
                    i++)
                {
                    Krilib.Control.CommandButtonRenderer.Draw(g, commandButtonArea, status.commands[i]);
                    commandButtonArea.X += Krilib.Control.CommandButtonRenderer.ButtonWHSize;
                    commandButtonArea.Width -= Krilib.Control.CommandButtonRenderer.ButtonWHSize;
                }
                if (unRect.Width <= 0) return;
            }
            //*******************************************
            //Name item rendering
            //*******************************************
            // Name(id:id)-[DateTime] via Service

            //Name
            Rectangle NameIdRect = unRect;
            string src = Core.Config.Appearance.Renderer.ShowUserId ?
                status.user.name + "(" + status.user.id + ")" :
                    status.user.name;
            NameIdRect.Width = TextRenderer.MeasureText(
                g,
                src,
                parent.Font,
                new Size(),
                TextFormatFlags.Left | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter).Width;
            if (NameIdRect.Width > unRect.Width)
                NameIdRect.Width = unRect.Width;
            unRect.X += NameIdRect.Width;
            unRect.Width -= NameIdRect.Width;
            var name = new TimelineCommonClickableItem(parent, NameIdRect);
            name.OnClick += new MouseEventHandler(name_OnClick);
            selectedItemMCItemList.Add(name);
            using (var f = new Font(parent.Font, FontStyle.Underline))
                TextRenderer.DrawText(
                    g,
                    src,
                    f,
                    NameIdRect,
                    status.ParentService.ForeColor,
                    TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);
            if (unRect.Width <= 0) return;

            //DateTime
            Rectangle dtRect = unRect;
            src = " - [" + status.postedDate.ToString(Core.Config.Appearance.Renderer.DateTimeFormat) + "] via ";
            dtRect.Width = TextRenderer.MeasureText(
                g,
                src,
                parent.Font,
                new Size(),
                TextFormatFlags.Left | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter).Width;
            if (dtRect.Width > unRect.Width)
                dtRect.Width = unRect.Width;
            unRect.X += dtRect.Width;
            unRect.Width -= dtRect.Width;
            TextRenderer.DrawText(
                g,
                src,
                parent.Font,
                dtRect,
                status.ParentService.ForeColor,
                TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);
            if (unRect.Width <= 0) return;

            //Service
            Rectangle SvcRect = unRect;
            SvcRect.Width = TextRenderer.MeasureText(
                g,
                status.ParentService.Name,
                parent.Font,
                new Size(),
                TextFormatFlags.Left | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter).Width;
            if (SvcRect.Width > unRect.Width)
                SvcRect.Width = unRect.Width;
            unRect.X += SvcRect.Width;
            unRect.Width -= SvcRect.Width;
            var svc = new TimelineCommonClickableItem(parent, SvcRect);
            svc.OnClick += new MouseEventHandler(svc_OnClick);
            selectedItemMCItemList.Add(svc);
            using (var f = new Font(parent.Font, FontStyle.Underline))
                TextRenderer.DrawText(
                    g,
                    status.ParentService.Name,
                    f,
                    SvcRect,
                    status.ParentService.ForeColor,
                    TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);
        }

        private void DrawSelectedStatusReplyArea(Graphics g, Status status, ref Rectangle rect)
        {
            if (status.replyData == null) return;
            Rectangle replyDataRect = new Rectangle(rect.X, rect.Y, rect.Width, Core.Config.Appearance.Renderer.Detail.NormalListHeight);
            g.FillRectangle(Brushes.Wheat, replyDataRect);
            rect.Y += replyDataRect.Height;
            rect.Height -= replyDataRect.Height;
            var tlc = new TimelineCommonClickableItem(parent, replyDataRect);
            tlc.OnClick += new MouseEventHandler((o, e) =>
            {
                try
                {
                    if (status.replyData.replyToId == null)
                        MessageBox.Show("返信先を特定できません。", "リプライジャンプ",
                            MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    else if (!parent.SelectIndexByUID(status.ParentAgent.Status.GetUidFromServiceId(status.replyData.replyToId)))
                        MessageBox.Show("該当するステータスはKrile中に見つかりません。", "リプライジャンプ",
                            MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("何らかのエラーにより、返信先を取得できませんでした:" + Environment.NewLine + ex.Message, "リプライジャンプ",
                        MessageBoxButtons.OK, MessageBoxIcon.Hand);
                }
            });
            StringBuilder repText = new StringBuilder();
            if (status.replyData.replyToUserName != null)
                repText.Append(status.replyData.replyToUserName);
            else
                repText.Append("[UNKNOWN]");
            repText.Append(">");
            if (status.replyData.replyToText != null)
                repText.Append(status.replyData.replyToText);
            else
                repText.Append("This message is not readable.");
            TextRenderer.DrawText(g, repText.ToString(), parent.Font, replyDataRect,
                Core.Config.Appearance.Renderer.Colors.ListReplyTextColor.Value,
                TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);
            selectedItemMCItemList.Add(tlc);
        }

        void svc_OnClick(object sender, MouseEventArgs e)
        {
            MessageBox.Show("Service clicked.");
        }

        void name_OnClick(object sender, MouseEventArgs e)
        {
            MessageBox.Show("Name clicked.");
        }

        private Image GetImageWithTLCtlAutomation(Uri u, bool refresh)
        {
            Image i;
            if (!refresh && Core.ImageManager.IsCacheExists(u))
            {
                i = Core.ImageManager.GetImage(u, refresh);
            }
            else
            {
                Core.ImageManager.GetImageCallback(
                    u,
                    refresh,
                    new Krilib.Bridges.GetImageCallbackDelegate((url, img) => { Core.FormManager.MainForm.RefreshTL(false); }));
                i = Properties.Resources.loading;
            }

            if (i == null)
                i = Properties.Resources.fail;
            return i;
        }

        Data.XStatus ps = null;
        public void UpdateMeasuring(int index)
        {
            if (index == -1)
            {
                SelectedItemHeight = 0;
                return;
            }
            if (ps != null && parent.Statuses[index] != ps)
            {
                foreach(var i in ps.BaseStatus.text.MessageObjects)
                    i.Reset();
                ps = null;
            }
            ps = parent.Statuses[index];
            int renderableWidth = parent.Width;
            renderableWidth -= Core.Config.Appearance.Renderer.UserNameArea;
            renderableWidth -= Core.Config.Appearance.Renderer.Detail.TextRenderingMargin * 2;
            Rectangle cur = new Rectangle(0, 0, renderableWidth, 0);
            ps.BaseStatus.text.Measure(
                renderableWidth,
                Core.Config.Appearance.Renderer.Detail.SelectedListMinHeight,
                Krilib.Control.XString.DrawMode.None);
            SelectedItemHeight = ps.BaseStatus.text.DrawSize.Height;
            SelectedItemHeight += Core.Config.Appearance.Renderer.Detail.TextRenderingMargin * 2;
            SelectedItemHeight += Core.Config.Appearance.Renderer.Detail.NormalListHeight;
            if (ps.BaseStatus.replyData != null) //reply data area
                SelectedItemHeight += Core.Config.Appearance.Renderer.Detail.NormalListHeight;
            if (SelectedItemHeight < Core.Config.Appearance.Renderer.Detail.SelectedListMinHeight)
                SelectedItemHeight = Core.Config.Appearance.Renderer.Detail.SelectedListMinHeight;
        }

        public IEnumerable<TimelineMouseControllableItem> MCItemFactory()
        {
            if (parent.Statuses == null || parent.Statuses.Length == 0) yield break;
            int vp = -(parent.VerticalPosition);
            int pvp = vp;
            for (int idx = 0; idx < parent.Statuses.Length; idx++)
            {
                if (idx == parent.SelectedIndex)
                {
                    if (vp + SelectedItemHeight > 0)
                    {
                        foreach (var i in parent.SelectedStatus.BaseStatus.text.MessageObjects)
                        {
                            if (parent.SelectedStatus.BaseStatus.replyData != null)
                                i.ItemShiftOffset.Y = vp + Core.Config.Appearance.Renderer.Detail.NormalListHeight * 2;
                            else
                                i.ItemShiftOffset.Y = vp + Core.Config.Appearance.Renderer.Detail.NormalListHeight;
                            i.ItemShiftOffset.X = Core.Config.Appearance.Renderer.UserNameArea;
                            yield return i;
                        }

                        foreach (var i in selectedItemMCItemList)
                            yield return i;

                        yield return new TimelineSelectedMCItem(
                            parent, idx,
                            new Rectangle(0, vp > 0 ? vp : 0,
                                parent.Width, vp > 0 ? SelectedItemHeight : SelectedItemHeight + vp));
                    }
                    vp += SelectedItemHeight;
                }
                else
                {
                    if (vp + NormalItemHeight > 0)
                    {
                        yield return new TimelineDefaultMCItem(
                            parent, idx,
                            new Rectangle(0, vp > 0 ? vp : 0,
                                parent.Width, vp > 0 ? NormalItemHeight : NormalItemHeight + vp));
                    }
                    vp += NormalItemHeight;
                }
                if (vp > parent.Height)
                    break;
            }
        }

        public class TimelineDefaultMCItem : TimelineMouseControllableItem
        {
            readonly TimelineList parent;

            public TimelineDefaultMCItem(TimelineList par ,int idx, Rectangle area)
            {
                parent = par;
                ItemIndex = idx;
                ItemArea = area;
            }

            public int ItemIndex { get; set; }
            public Rectangle ItemArea { get; set; }
            public override IEnumerable<Rectangle> ItemRect
            {
                get { yield return ItemArea; }
            }

            public override string Helptip
            {
                get
                {
                    Status target = parent.Statuses[ItemIndex].BaseStatus;
                    StringBuilder sb = new StringBuilder();
                    sb.Append(target.user.name);
                    sb.Append(" : ");
                    if (target.text.HeaderText != null)
                        sb.Append("[" + target.text.HeaderText + "]");
                    string targetText = target.text.Original;
                    while (targetText != String.Empty)
                    {
                        if (targetText.Length > Core.Config.Appearance.Renderer.TooltipNewlineWidth)
                        {
                            sb.Append(targetText.Substring(0, Core.Config.Appearance.Renderer.TooltipNewlineWidth));
                            targetText = targetText.Substring(Core.Config.Appearance.Renderer.TooltipNewlineWidth);
                            sb.Append(Environment.NewLine);
                        }
                        else
                        {
                            sb.Append(targetText);
                            targetText = String.Empty;
                        }
                    }
                    if (target.text.FooterText != null)
                        sb.Append("[" + target.text.FooterText + "]");
                    return sb.ToString();
                }
            }

            public override void OnMouseClick(object sender, ChainedMouseEventArgs e)
            {
                parent.SelectedIndex = ItemIndex;
            }
        }

        public class TimelineSelectedMCItem : TimelineMouseControllableItem
        {
            readonly TimelineList parent;

            public TimelineSelectedMCItem(TimelineList par, int idx, Rectangle area)
            {
                parent = par;
                ItemIndex = idx;
                ItemArea = area;
            }

            public int ItemIndex { get; set; }
            public Rectangle ItemArea { get; set; }
            public override IEnumerable<Rectangle> ItemRect
            {
                get { yield return ItemArea; }
            }

            public override void OnMouseClick(object sender, ChainedMouseEventArgs e)
            {
                if (e.Button == MouseButtons.Right)
                {
                    MessageBox.Show("Context menu");
                }
            }
        }

        public class TimelineCommonClickableItem : TimelineMouseControllableItem
        {
            readonly TimelineList parent;

            public TimelineCommonClickableItem(TimelineList par, Rectangle rgn)
            {
                parent = par;
                RegionRect = rgn;
            }

            Rectangle RegionRect { get; set; }
            public override IEnumerable<Rectangle> ItemRect
            {
                get { yield return RegionRect; }
            }

            public event MouseEventHandler OnClick;
            public override void OnMouseClick(object sender, ChainedMouseEventArgs e)
            {
                if (OnClick != null)
                {
                    OnClick.Invoke(sender, (MouseEventArgs)e);
                }
            }

            public override void OnMouseEnter(object sender, ChainedMouseEventArgs e)
            {
                Core.FormManager.MainForm.SetCursor(Cursors.Hand);
            }

            public override void OnMouseLeave(object sender, ChainedMouseEventArgs e)
            {
                Core.FormManager.MainForm.SetCursor(Cursors.Default);
            }
        }

    }
}
